package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.Voucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.UserAccountMapper;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.service.IVoucherService;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import com.hmdp.utils.redis.impl.SimpleRedisLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

import static com.hmdp.utils.RedisConstants.LOCK_KEY_PRE;

/**
 *  服务实现类
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    //注入Id生成器
    @Resource
    private RedisIdWorker redisIdWorker;
    //注入秒杀券服务
    @Resource
    private ISeckillVoucherService seckillVoucherService;
    //注入RedisStringTemplate
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    //注入Redisson
    @Resource
    private RedissonClient redissonClient;
    //注入用户账户mapper
    @Autowired(required = false)
    private UserAccountMapper userAccountMapper;

    //开启单个线程的线程池
    private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();
//    private static final ExecutorService SECKILL_ORDER_VIP_EXECUTOR = Executors.newSingleThreadExecutor();

    //PostConstruct注解是使该方法在当前类初始化完后就执行
    @PostConstruct
    private void init(){
        //商铺界面抢购
        SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
        //用户积分抢购
//        SECKILL_ORDER_VIP_EXECUTOR.submit(new VoucherOrderHandler_vip());
    }
    private class VoucherOrderHandler implements Runnable{

        @Override
        public void run() {
            String stream = "stream.orders";
            //死循环，一直从阻塞队列中取订单信息
            while(true){
                try{
                    // 1.获取消息队列中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS stream.orders >
                    List<MapRecord<String,Object,Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1","c1"),
                            StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                            StreamOffset.create(stream, ReadOffset.lastConsumed()) // ">"表示从下一个未消费的消息开始
                    );
                    // 2.判断订单信息是否为空
                    if(list == null || list.isEmpty()){ //StringUtils.isEmpty(null) == true
                        // 如果为null，说明没有消息，继续下一次循环
                        continue;
                    }
                    // 解析消息
                    MapRecord<String, Object, Object> record = list.get(0);//获取list中从消息队列中取到的暂时排第一个的消息
                    Map<Object, Object> value = record.getValue();
                    // 将value转为VoucherOrder，利用hutool工具类，fillBeanWithMap
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                    // 3.创建订单
                    createVoucherOrder(voucherOrder);
                    // 4.确认消息 XACK stream.order g1 id
                    stringRedisTemplate.opsForStream().acknowledge(stream,"g1",record.getId());
                } catch (Exception e){
                    //如果有异常，则记录日志
                    log.error("处理订单异常: ", e);
                    //从pending-list中获取已消费但是没确认的消息，例如0，是从pending-list中的第一个消息开始
                    handlePendingList();
                }
            }
        }

        //从pending-list中获取已消费但是没确认的消息，例如0，是从pending-list中的第一个消息开始
        private void handlePendingList() {
            String stream = "stream.orders";
            //死循环，一直从阻塞队列中取订单信息
            while(true){
                try{
                    // 1.获取pending-list中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS stream.orders 0
                    List<MapRecord<String,Object,Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1","c1"),
                            StreamReadOptions.empty().count(1),
                            StreamOffset.create(stream, ReadOffset.from("0"))
                    );
                    // 2.判断订单信息是否为空
                    if(list == null || list.isEmpty()){ //StringUtils.isEmpty(null) == true
                        // 如果为null，说明没有消息，结束
                        break;
                    }
                    // 解析消息
                    MapRecord<String, Object, Object> record = list.get(0);//获取list中从消息队列中取到的暂时排第一个的消息
                    Map<Object, Object> value = record.getValue();
                    // 将value转为VoucherOrder，利用hutool工具类，fillBeanWithMap
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                    // 3.创建订单
                    createVoucherOrder(voucherOrder);
                    // 4.确认消息 XACK stream.order g1 id
                    stringRedisTemplate.opsForStream().acknowledge(stream,"g1",record.getId());
                } catch (Exception e){
                    //如果有异常，则记录日志
                    log.error("(处理pending-list消息队列)处理订单异常: ", e);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException interruptedException) {
                        interruptedException.printStackTrace();
                    }
                }
            }
        }
    }

    //处理积分抢购优惠券
    /*private class VoucherOrderHandler_vip implements Runnable{

        @Override
        public void run() {
            String stream = "stream.orders";
            //死循环，一直从阻塞队列中取订单信息
            while(true){
                try{
                    // 1.获取消息队列中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS stream.orders >
                    List<MapRecord<String,Object,Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1","c1"),
                            StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                            StreamOffset.create(stream, ReadOffset.lastConsumed()) // ">"表示从下一个未消费的消息开始
                    );
                    // 2.判断订单信息是否为空
                    if(list == null || list.isEmpty()){ //StringUtils.isEmpty(null) == true
                        // 如果为null，说明没有消息，继续下一次循环
                        continue;
                    }
                    // 解析消息
                    MapRecord<String, Object, Object> record = list.get(0);//获取list中从消息队列中取到的暂时排第一个的消息
                    Map<Object, Object> value = record.getValue();
                    // 将value转为VoucherOrder，利用hutool工具类，fillBeanWithMap
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                    // 3.创建订单
                    createVoucherOrder_vip(voucherOrder);
                    // 4.确认消息 XACK stream.order g1 id
                    stringRedisTemplate.opsForStream().acknowledge(stream,"g1",record.getId());
                } catch (Exception e){
                    //如果有异常，则记录日志
                    log.error("处理订单异常: ", e);
                    //从pending-list中获取已消费但是没确认的消息，例如0，是从pending-list中的第一个消息开始
                    handlePendingList();
                }
            }
        }

        //从pending-list中获取已消费但是没确认的消息，例如0，是从pending-list中的第一个消息开始
        private void handlePendingList() {
            String stream = "stream.orders";
            //死循环，一直从阻塞队列中取订单信息
            while(true){
                try{
                    // 1.获取pending-list中的订单信息 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS stream.orders 0
                    List<MapRecord<String,Object,Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1","c1"),
                            StreamReadOptions.empty().count(1),
                            StreamOffset.create(stream, ReadOffset.from("0"))
                    );
                    // 2.判断订单信息是否为空
                    if(list == null || list.isEmpty()){ //StringUtils.isEmpty(null) == true
                        // 如果为null，说明没有消息，结束
                        break;
                    }
                    // 解析消息
                    MapRecord<String, Object, Object> record = list.get(0);//获取list中从消息队列中取到的暂时排第一个的消息
                    Map<Object, Object> value = record.getValue();
                    // 将value转为VoucherOrder，利用hutool工具类，fillBeanWithMap
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                    // 3.创建订单
                    createVoucherOrder(voucherOrder);
                    // 4.确认消息 XACK stream.order g1 id
                    stringRedisTemplate.opsForStream().acknowledge(stream,"g1",record.getId());
                } catch (Exception e){
                    //如果有异常，则记录日志
                    log.error("(处理pending-list消息队列)处理订单异常: ", e);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException interruptedException) {
                        interruptedException.printStackTrace();
                    }
                }
            }
        }
    }*/


    //JDK阻塞队列
    /*private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);
    private class VoucherOrderHandler implements Runnable{

        @Override
        public void run() {
            //死循环，一直从阻塞队列中取订单信息
            while(true){
                try{
                    //有订单信息就取出，没有则阻塞（当secKillVoucher（voucherId）执行完即可以取到）
                    VoucherOrder voucherOrder = orderTasks.take();
                    //创建订单
                    createVoucherOrder(voucherOrder);
                } catch (Exception e){
                    //如果有异常，则记录日志
                    log.error("处理订单异常: ", e);
                }

            }
        }
    }*/

    //创建订单
    private void createVoucherOrder(VoucherOrder voucherOrder) {
        //从订单信息中获取用户id、优惠券id
        Long userId = voucherOrder.getUserId();
        Long voucherId = voucherOrder.getVoucherId();
        //创建Redis锁对象
        RLock redisson_lock = redissonClient.getLock(LOCK_KEY_PRE + "seckill:" + userId);
        //尝试获取锁
        boolean lock = redisson_lock.tryLock(); //如果选择空参，也就是非阻塞队列，等待时间默认为-1就是不等待，释放时间默认是30s
        //判断是否获取锁
        if(!lock){
            //获取锁失败，返回错误信息或重试(递归)
            log.error("获取锁失败，不允许重复下单!");
            return ;
        }
        //一下是兜底，防止出现未预料到的线程并发安全问题，此前一人一单，库存判断已经在秒杀业务中从Redis中判断过了，redis读写速度快，一般不会出现问题
        try {
            // 5.1.查询订单,不需要查出具体数据，只需要查出有无这条数据，用count()
            Integer count = this.query().eq("user_id", userId).eq("voucher_id", voucherId).count();
            // 5.2.判断数据用户是否已经下过单
            if (count > 0) {
                log.error("不允许重复下单!");
                return ;
            }

            boolean flag = seckillVoucherService.update()
                    .setSql("stock = stock - 1") // set stock = stock - 1
                    .eq("voucher_id", voucherId).gt("stock", 0) // where voucher_id = ? and stock > 0
                    .update();
            if (!flag) {
                // 扣减失败
                log.error("秒杀券库存不足!");
                return ;
            }

            // 8.将订单写入数据库
            this.save(voucherOrder);
        } finally {
            //释放锁
            redisson_lock.unlock();
        }
    }

    //VIP用户抢优惠券: 创建订单、扣减用户积分(这里涉及两个表的操作，应该添加事务注解，但是事务注解必须加在有@Override上，因此后面需要修改函数)
    /*private void createVoucherOrder_vip(VoucherOrder voucherOrder) {
        //从订单信息中获取用户id、优惠券id
        Long userId = voucherOrder.getUserId();
        Long voucherId = voucherOrder.getVoucherId();
        //创建Redis锁对象
        RLock redisson_lock = redissonClient.getLock(LOCK_KEY_PRE + "seckill:" + userId);
        //尝试获取锁
        boolean lock = redisson_lock.tryLock(); //如果选择空参，也就是非阻塞队列，等待时间默认为-1就是不等待，释放时间默认是30s
        //判断是否获取锁
        if(!lock){
            //获取锁失败，返回错误信息或重试(递归)
            log.error("获取锁失败，不允许重复下单!");
            return ;
        }
        //一下是兜底，防止出现未预料到的线程并发安全问题，此前一人一单，库存判断已经在秒杀业务中从Redis中判断过了，redis读写速度快，一般不会出现问题
        try {
            // 5.1.查询订单,不需要查出具体数据，只需要查出有无这条数据，用count()
            Integer count = this.query().eq("user_id", userId).eq("voucher_id", voucherId).count();
            // 5.2.判断数据用户是否已经下过单
            if (count > 0) {
                log.error("不允许重复下单!");
                return ;
            }

            boolean flag = seckillVoucherService.update()
                    .setSql("stock = stock - 1") // set stock = stock - 1
                    .eq("voucher_id", voucherId).gt("stock", 0) // where voucher_id = ? and stock > 0
                    .update();
            if (!flag) {
                // 扣减失败
                log.error("秒杀券库存不足!");
                return ;
            }

            // 8.将订单写入数据库
            this.save(voucherOrder);

            // 9.扣减用户库存
            this.userAccountMapper.updatePointOfVipUser(userId);
        } finally {
            //释放锁
            redisson_lock.unlock();
        }
    }*/

    // 设置Lua脚本的...
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT_VIP; //vip用户根据积分抢优惠券
    static{
        // 初始化
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        // 加载lua文件资源
//        SECKILL_SCRIPT.setLocation(new ClassPathResource("script\\seckill.lua"));//基于JDK阻塞队列的lua脚本
        SECKILL_SCRIPT.setLocation(new ClassPathResource("script\\seckill2.lua"));
        // 设置返回值类型
        SECKILL_SCRIPT.setResultType(Long.class);
    }
    static{
        // 初始化
        SECKILL_SCRIPT_VIP = new DefaultRedisScript<>();
        // 加载lua文件资源
        SECKILL_SCRIPT_VIP.setLocation(new ClassPathResource("script\\vipSeckill.lua"));
        // 设置返回值类型
        SECKILL_SCRIPT_VIP.setResultType(Long.class);
    }

    /**
     * 基于Lua脚本和Redis中的Stream队列
     * @param voucherId
     * @return
     */
    @Override
    public Result secKillVoucher(Long voucherId) {
        // 获取当前用户id
        Long userId = UserHolder.getUser().getId();
        // 生成订单id
        long orderId = redisIdWorker.nextId("order");
        // 1.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), userId.toString(), String.valueOf(orderId)
        );
        // 将lua脚本返回的结果拆箱
        int res = result.intValue();
        // 2.判断结果是否为0
        if(res != 0){
            // 2.1.补位0， 代表没有购买资格
            return Result.fail(res == 1 ? "秒杀券库存不足!" : "不能重复抢购秒杀券!");
        }
        // 3.返回订单id
        return Result.ok(orderId);
    }

    /**
     * 基于Lua脚本和Redis中的Stream队列实现VIP用户抢优惠券功能
     * Lua脚本能保证查询缓存的原子性
     * @param voucherId
     * @return
     */
    @Override
    public Result seckillVipVoucher(Long voucherId) {
        // 0.判断秒杀是否开始
        //        // 1.查询优惠券
//        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
//
//        // 2.判断秒杀是否开始
//        if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
//            // 秒杀未开始，返回
//            return Result.fail("秒杀活动尚未开始!");
//        }
//
//        // 3.判断秒杀是否已经结束
//        if(voucher.getEndTime().isBefore(LocalDateTime.now())){
//            // 秒杀已经结束，返回
//            return Result.fail("秒杀活动已经结束!");
//        }
        // 1.获取当前用户id
        Long userId = UserHolder.getUser().getId();
        // 2.生成订单id
        Long orderId = redisIdWorker.nextId("order");
        // 3.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT_VIP, //脚本文件信息
                Collections.emptyList(), //KEYS[]
                voucherId.toString(), userId.toString(), String.valueOf(orderId)
        );
        // 4.将lua脚本返回的结果拆箱
        int res = result.intValue();
        // 5.判断结果
        if(res != 0){
            // 5.1.补位0，代表没有购买资格
            if(res == 1){
                return Result.fail("用户不是VIP，没有购买资格!");
            }else if(res == 2){
                return Result.fail("不能重复抢购优惠券!");
            }else if(res == 3){
                return Result.fail("优惠券已经被抢完!");
            }else if(res == 4){
                return Result.fail("积分不足!");
            }
        }
        // ————————还没决定好数据库扣积分
        // 6.返回订单id
        return Result.ok(orderId);
    }

    /**
     * 基于Lua脚本,利用JDK的阻塞队列来实现
     * @param voucherId
     * @return
     */
//    @Override
//    public Result secKillVoucher(Long voucherId) {
//        // 获取当前用户id
//        Long userId = UserHolder.getUser().getId();
//        // 1.执行lua脚本
//        Long result = stringRedisTemplate.execute(
//                SECKILL_SCRIPT,
//                Collections.emptyList(),
//                voucherId.toString(), userId.toString()
//        );
//        // 将lua脚本返回的结果拆箱
//        int res = result.intValue();
//        // 2.判断结果是否为0
//        if(res != 0){
//            // 2.1.补位0， 代表没有购买资格
//            return Result.fail(res == 1 ? "秒杀券库存不足!" : "不能重复抢购秒杀券!");
//        }
//        // 2.2.为0，有购买资格，把下单信息保存到阻塞队列
//        // 2.3.生成订单id
//        long orderId = redisIdWorker.nextId("order");
//        // 2.4.设置订单信息
//        VoucherOrder voucherOrder = new VoucherOrder();
//        voucherOrder.setId(orderId); //设置订单id
//        voucherOrder.setUserId(userId); //设置用户id
//        voucherOrder.setVoucherId(voucherId); //设置优惠券id
//        // TODO 保存订单信息到阻塞队列
//        // 2.5.创建阻塞队列
//        orderTasks.add(voucherOrder);
//        // 3.返回订单id
//        return Result.ok(orderId);
//    }

    /**
     *
     * @param voucherId 正在抢购的优惠券Id
     * @return
     */
//    @Override
//    public Result secKillVoucher(Long voucherId) {
//        // 1.查询优惠券
//        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
//
//        // 2.判断秒杀是否开始
//        if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
//            // 秒杀未开始，返回
//            return Result.fail("秒杀活动尚未开始!");
//        }
//
//        // 3.判断秒杀是否已经结束
//        if(voucher.getEndTime().isBefore(LocalDateTime.now())){
//            // 秒杀已经结束，返回
//            return Result.fail("秒杀活动已经结束!");
//        }
//
//        // 4.判断库存是否充足
//        Integer stock = voucher.getStock();
//        // 采用乐观锁，将stock看作是"版本号"
//        if(stock <= 0){
//            // 库存不足
//            return Result.fail("秒杀券已经被抢光!");
//        }
//
//        //解决判断同一用户是否存在订单重复，采用悲观锁来实现一人一单
//        //解决判断同一用户是否存在订单重复，采用分布式锁简单版来实现一人一单
//        return createVoucherOrder(voucherId);
//
//    }

    /**
     * 采用Redisson
     * 可以实现重入、重试（重试需要传入等待时间，不填默认为-1也就是不等待，失败就返回false）
     * @param voucherId
     * @return
     */
//    @Transactional //添加事务注解，保证原子性（涉及两张表的改变）
//    public Result createVoucherOrder(Long voucherId) {
//        // 5.一人一单
//        // 获取本地线程中的用户Id
//        Long userId = UserHolder.getUser().getId();
//
//        //创建Redis锁对象
////        SimpleRedisLock simpleLock = new SimpleRedisLock("seckill:" + userId, stringRedisTemplate);
//        RLock redisson_lock = redissonClient.getLock(LOCK_KEY_PRE + "seckill:" + userId);
//
//        //尝试获取锁
////        boolean lock = simpleLock.tryLock(1200L);
//        boolean lock = redisson_lock.tryLock(); //如果选择空参，也就是非阻塞队列，等待时间默认为-1就是不等待，释放时间默认是30s
//        //判断是否获取锁
//        if(!lock){
//            //获取锁失败，返回错误信息或重试(递归)
//            return Result.fail("不能重复抢购秒杀券!");
//        }
//        try {
//            // 5.1.查询订单,不需要查出具体数据，只需要查出有无这条数据，用count()
//            Integer count = this.query().eq("user_id", userId).eq("voucher_id", voucherId).count();
//            // 5.2.判断数据用户是否已经下过单
//            if (count > 0) {
//                return Result.fail("不能重复抢购秒杀券!");
//            }
//
//            boolean flag = seckillVoucherService.update()
//                    .setSql("stock = stock - 1") // set stock = stock - 1
//                    .eq("voucher_id", voucherId).gt("stock", 0) // where voucher_id = ? and stock > 0
//                    .update();
//            if (!flag) {
//                // 扣减失败
//                return Result.fail("秒杀券已经被抢光!");
//            }
//
//            // 7.创建订单
//            VoucherOrder voucherOrder = new VoucherOrder();
//            // 7.1.订单id
//            Long orderId = redisIdWorker.nextId("order");
//            voucherOrder.setId(orderId);
//            // 7.2.用户id
//            voucherOrder.setUserId(userId);
//            // 7.3.代金券id
//            voucherOrder.setVoucherId(voucherId);
//
//            // 8.将订单写入数据库
//            this.save(voucherOrder);
//
//            // 9.返回订单id
//            return Result.ok(orderId);
//
//        } finally {
//            //释放锁
//            redisson_lock.unlock();
//        }
//    }


    /**
     * 一人一单，采用分布式锁简单版，用Redis中的SETNX互斥机制，能在多服务器上生效
     * @param voucherId
     * @return
     */
//    @Transactional //添加事务注解，保证原子性（涉及两张表的改变）
//    public Result createVoucherOrder(Long voucherId) {
//        // 5.一人一单
//        // 获取本地线程中的用户Id
//        Long userId = UserHolder.getUser().getId();
//
//        //创建Redis锁对象
//        SimpleRedisLock simpleLock = new SimpleRedisLock("seckill:" + userId, stringRedisTemplate);
//        //尝试获取锁
//        boolean lock = simpleLock.tryLock(1200L);
//        //判断是否获取锁
//        if(!lock){
//            //获取锁失败，返回错误信息或重试(递归)
//            return Result.fail("不能重复抢购秒杀券!");
//        }
//        try {
//            // 5.1.查询订单,不需要查出具体数据，只需要查出有无这条数据，用count()
//            Integer count = this.query().eq("user_id", userId).eq("voucher_id", voucherId).count();
//            // 5.2.判断数据用户是否已经下过单
//            if (count > 0) {
//                return Result.fail("不能重复抢购秒杀券!");
//            }
//
//            boolean flag = seckillVoucherService.update()
//                    .setSql("stock = stock - 1") // set stock = stock - 1
//                    .eq("voucher_id", voucherId).gt("stock", 0) // where voucher_id = ? and stock > 0
//                    .update();
//            if (!flag) {
//                // 扣减失败
//                return Result.fail("秒杀券已经被抢光!");
//            }
//
//            // 7.创建订单
//            VoucherOrder voucherOrder = new VoucherOrder();
//            // 7.1.订单id
//            Long orderId = redisIdWorker.nextId("order");
//            voucherOrder.setId(orderId);
//            // 7.2.用户id
//            voucherOrder.setUserId(userId);
//            // 7.3.代金券id
//            voucherOrder.setVoucherId(voucherId);
//
//            // 8.将订单写入数据库
//            this.save(voucherOrder);
//
//            // 9.返回订单id
//            return Result.ok(orderId);
//
//        } finally {
//            //释放锁
//            simpleLock.unLock();
//        }
//    }

    /**
     * 一人一单，采用java中的悲观锁synchronize，锁关键字
     * 但这种机制只能在单服务器实现，多服务器负载则失效
     * @param voucherId
     * @return
     */
//    @Transactional //添加事务注解，保证原子性（涉及两张表的改变）
//    public Result createVoucherOrder(Long voucherId) {
//        // 5.一人一单
//        Long userId = UserHolder.getUser().getId();
//        synchronized(userId.toString().intern()){
//            // 5.1.查询订单,不需要查出具体数据，只需要查出有无这条数据，用count()
//            Integer count = this.query().eq("user_id", userId).eq("voucher_id", voucherId).count();
//            // 5.2.判断数据用户是否已经下过单
//            if(count > 0){
//                return Result.fail("不能重复抢购秒杀券!");
//            }
//
//            // 6.扣减库存（对比版本号前后是否相同）
////        voucher.setStock(stock - 1);
////        seckillVoucherService.updateById(voucher);
//            boolean flag = seckillVoucherService.update()
//                    .setSql("stock = stock - 1") // set stock = stock - 1
//                    .eq("voucher_id", voucherId).gt("stock",0) // where voucher_id = ? and stock > 0
//                    .update();
//            if(!flag){
//                // 扣减失败
//                return Result.fail("秒杀券已经被抢光!");
//            }
//
//            // 7.创建订单
//            VoucherOrder voucherOrder = new VoucherOrder();
//            // 7.1.订单id
//            Long orderId = redisIdWorker.nextId("order");
//            voucherOrder.setId(orderId);
//            // 7.2.用户id
//            voucherOrder.setUserId(userId);
//            // 7.3.代金券id
//            voucherOrder.setVoucherId(voucherId);
//
//            // 8.将订单写入数据库
//            this.save(voucherOrder);
//
//            // 8.返回订单id
//            return Result.ok(orderId);
//        }
//
//    }
}
